/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.opmviewer.converters;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.openprovenance.model.Artifact;
import org.openprovenance.model.Process;
import org.openprovenance.model.OPMGraph;
import org.openprovenance.model.Used;
import org.openprovenance.model.WasGeneratedBy;
import org.openprovenance.model.WasTriggeredBy;
import org.opmviewer.valueproviders.DefaultValueProvider;
import org.opmviewer.valueproviders.Value;
import org.opmviewer.valueproviders.ValueProvider;

/**
 *
 * @author grzegorz
 */
public class OPM2Neo4j {

    Map<String, Node> visited;
    EmbeddedGraphDatabase graphDb;
    ValueProvider value_provider;

    public OPM2Neo4j(ValueProvider vp) {
        if (vp == null) {
            value_provider = new DefaultValueProvider();
        } else {
            value_provider = vp;
        }

    }

    private void createNeo4jGraph(OPMGraph opmg, String dbName, String dbLoc) {
        Long id = 0l;
        visited = new HashMap<String, Node>();

        graphDb = new EmbeddedGraphDatabase(dbLoc
                + System.getProperty("file.separator") + dbName);
        Transaction tx = graphDb.beginTx();
        System.err.println("START");
        int i = 0;
        try {
            for (Object o : opmg.getDependencies().
                    getUsedOrWasGeneratedByOrWasTriggeredBy()) {
                System.err.print("" + i + " ");
                ++i;
                if (o instanceof Used) {

                    Used u = (Used) o;

                    Artifact cause = (Artifact) u.getCause().getRef();
                    Node art = visited.containsKey(cause.getId())
                            ? visited.get(cause.getId()) : addArtifact(cause);

                    Process effect = (Process) u.getEffect().getRef();
                    Node proc = visited.containsKey(effect.getId())
                            ? visited.get(effect.getId()) : addProcess(effect);
                    proc.createRelationshipTo(art, OPMRelationshipType.Used).
                            setProperty("role", "some-role");

                } else if (o instanceof WasGeneratedBy) {
                    WasGeneratedBy wgb = (WasGeneratedBy) o;

                    Artifact effect = (Artifact) wgb.getEffect().getRef();
                    Node art = visited.containsKey(effect.getId())
                            ? visited.get(effect.getId()) : addArtifact(effect);

                    Process cause = (Process) wgb.getCause().getRef();
                    Node proc = visited.containsKey(cause.getId())
                            ? visited.get(cause.getId()) : addProcess(cause);
                    art.createRelationshipTo(proc,
                            OPMRelationshipType.WasGeneratedBy).setProperty("role", "some-role");

                } else if (o instanceof WasTriggeredBy) {
                    WasTriggeredBy wtb = (WasTriggeredBy) o;

                    Process cause = (Process) wtb.getCause().getRef();
                    Node proc1 = visited.containsKey(cause.getId())
                            ? visited.get(cause.getId()) : addProcess(cause);

                    Process effect = (Process) wtb.getEffect().getRef();
                    Node proc2 = visited.containsKey(effect.getId())
                            ? visited.get(effect.getId()) : addProcess(effect);
                    proc1.createRelationshipTo(proc2,
                            OPMRelationshipType.WasTriggeredBy).setProperty("role", "some-role");
                }
            }
            Node root = graphDb.createNode();
            root.setProperty("type", "root");
            root.setProperty("name", "root");
            root.setProperty("value", "root");
            root.setProperty("vtype", "STRING");

            // attach one node from each connected component of the provenance
            // graph to a root node

            Set<Node> allNodes = new HashSet<Node>(visited.values());

            for (Node node : allNodes) {
                if(!node.hasRelationship(Direction.OUTGOING)){
                    node.createRelationshipTo(root,
                            OPMRelationshipType.RootLink).setProperty("role", "some-role");
                }
            }

            tx.success();
        } finally {
            tx.finish();
            graphDb.shutdown();
        }

    }

    public void createNeo4jGraph(String fileName, String dbName, String dbLoc) {

        OPMGraph opmg = null;
        OPMRdf2Xml rdf2xml = new OPMRdf2Xml();
        try {
            opmg = rdf2xml.convert(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        createNeo4jGraph(opmg, dbName, dbLoc);
    }

    private Node addProcess(Process p) {
        Node n = graphDb.createNode();
        n.setProperty("type", "process");
        n.setProperty("name", p.getId());
        Value v = value_provider.getValue(p.getId());
        n.setProperty("value",
                v.getValue());
        n.setProperty("vtype", v.getType().toString());
        visited.put(p.getId(), n);
        return n;
    }

    private Node addArtifact(Artifact a) {
        Node n = graphDb.createNode();
        n.setProperty("type", "artifact");
        n.setProperty("name", a.getId());
        Value v = value_provider.getValue(a.getId());
        n.setProperty("value",
                v.getValue());
        n.setProperty("vtype", v.getType().toString());
        visited.put(a.getId(), n);
        return n;
    }
}
